package com.shenmazong.listen;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.shenmazong.grade.entity.TbTaskGrade;
import com.shenmazong.grade.entity.TbTaskTheoryGradeDetail;
import com.shenmazong.grade.mapper.TbTaskGradeMapper;
import com.shenmazong.grade.mapper.TbTaskTheoryGradeDetailMapper;
import com.shenmazong.library.entity.TaskTheoryEs;
import com.shenmazong.library.mapper.TbTaskTheoryLibraryMapper;
import com.shenmazong.manage.entity.*;
import com.shenmazong.manage.mapper.*;
import com.shenmazong.util.MyTimeUtil;
import com.shenmazong.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.shenmazong.config.MqConfig.*;

/**
 * @author Qi
 * @version 1.0
 * @description TODO
 * @date 2021/9/28 9:12
 */
@Component
@Slf4j
public class MyListener {
    @Autowired
    ElasticsearchRestTemplate esTemplate;
    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    MongoTemplate mongoTemplate;
    @Autowired
    TbTaskTheoryLibraryMapper theoryMapper;
    @Autowired
    TbTaskLayoutMapper layoutMapper;
    @Autowired
    TbTaskClazzMapper taskClazzMapper;
    @Autowired
    TbTaskGradeMapper gradeMapper;
    @Autowired
    TbTaskTheoryTemplateMapper templateMapper;
    @Autowired
    TbTaskTheoryContentMapper contentMapper;
    @Autowired
    TbTaskTheoryGradeDetailMapper theoryDetailMapper;
    @Autowired
    TbTaskSubmitMapper submitMapper;

    //理论库同步队列
    @RabbitListener(queues = THEORY_QUEUE)
    public void theoryToEs1(String content){
        log.info("队列 {} 接收到的消息为:{}","THEORY_QUEUE",content);
        String[] split = content.split(",");

        try {
            if("save".equals(split[1])){
                //根据ID在数据库中获取数据
                TaskTheoryEs taskTheoryEs = theoryMapper.selectTheoryNotToEsById(Long.parseLong(split[0]));
                esTemplate.save(taskTheoryEs);
                //修改数据库同步状态
                theoryMapper.updateToEsById(Long.parseLong(split[0]));
            }else if ("del".equals(split[1])){
                esTemplate.delete(split[0],TaskTheoryEs.class);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.info("数据同步到ES失败,数据ID为:{}",split[0]);
        }
        log.info("数据同步到ES成功");
    }

    @RabbitListener(queues = THEORY_QUEUE)
    public void theoryToEs2(String content){
        log.info("队列 {} 接收到的消息为:{}","THEORY_QUEUE",content);
        String[] split = content.split(",");

        try {
            if("save".equals(split[1])){
                //根据ID在数据库中获取数据
                TaskTheoryEs taskTheoryEs = theoryMapper.selectTheoryNotToEsById(Long.parseLong(split[0]));
                esTemplate.save(taskTheoryEs);
                //修改数据库同步状态
                theoryMapper.updateToEsById(Long.parseLong(split[0]));
            }else if ("del".equals(split[1])){
                esTemplate.delete(split[0],TaskTheoryEs.class);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.info("数据同步到ES失败,数据ID为:{}",split[0]);
        }
        log.info("数据同步到ES成功");
    }

    //考试安排展示队列
    @RabbitListener(queues = TASK_LAYOUT_QUEUE)
    public void layoutToRedis(String content){

        try {
            ObjectMapper mapper = new ObjectMapper();
            TbTaskLayout layout = mapper.readValue(content, TbTaskLayout.class);

            //放入展示作业的Redis集合中
            String showTask = "show_task";
            redisTemplate.opsForSet().add(showTask,String.valueOf(layout.getLayoutId()));

            //获取当前时间时间戳
            long nowTime = System.currentTimeMillis();
            //将考试安排开始时间转为时间戳
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date date1 = sdf.parse(layout.getStartTime());
            //考试开始时间的时间戳
            long startTimeToLong = date1.getTime();
            //获取距离考试开始的时间 单位毫秒
            long startTime = startTimeToLong-nowTime;
            //考试结束时间的时间戳
            Date date2 = sdf.parse(layout.getEndTime());
            long endTimeToLong = date2.getTime();
            //获取距离考试结束的时间 单位毫秒
            long endTime = endTimeToLong-nowTime;

            //Redis中存放作业开始和结束的时间戳map
            Map<String, String> map = new HashMap<>();
            StartAndEndTimeVo startAndEndTimeVo = new StartAndEndTimeVo(startTimeToLong,endTimeToLong);
            String mapValue = mapper.writeValueAsString(startAndEndTimeVo);
            map.put(String.valueOf(layout.getLayoutId()),mapValue);
            String showTaskTime = "show_task_time_map";
            redisTemplate.opsForHash().putAll(showTaskTime,map);

            //将作业信息存入Redis
            String layoutKey = "task_layout_"+layout.getLayoutId();
            //修改考试安排数据
            layout.setShowStatus(1);
            layout.setUpdateTime(MyTimeUtil.getNowTimeToString());
            //存入Redis
            String value = mapper.writeValueAsString(layout);
            redisTemplate.opsForValue().set(layoutKey,value);
            //修改数据库
            layoutMapper.updateById(layout);

            //在Redis中存储考试状态
            String statusKey = "task_status_"+layout.getLayoutId();
            redisTemplate.opsForValue().set(statusKey,"0");

            //获取试题信息 存储Redis
            TbTaskTheoryContent tbTaskTheoryContent = contentMapper.selectById(layout.getTheoryContentId());
            Query query = new Query(Criteria.where("_id").is(tbTaskTheoryContent.getTheoryMongo()));
            ContentToMongo contentToMongo = mongoTemplate.findOne(query, ContentToMongo.class);
            String contentKey = "task_content_"+layout.getLayoutId();
            redisTemplate.opsForValue().set(contentKey,contentToMongo.getContent());

            //Redis中存储班级作业信息
            List<TbTaskClazz> clazzList = taskClazzMapper.selectList(
                    new QueryWrapper<TbTaskClazz>().lambda().eq(TbTaskClazz::getTaskId, layout.getLayoutId())
            );
            if(clazzList.size()!=0){
                for (TbTaskClazz taskClazz : clazzList) {
                    String key = "task_layout_clazz_"+taskClazz.getClazzId();
                    redisTemplate.opsForSet().add(key,String.valueOf(layout.getLayoutId()));
                }
            }

            //设置Redis中所有key的过期时间
            redisTemplate.expire(layoutKey,endTime, TimeUnit.MILLISECONDS);
            redisTemplate.expire(statusKey,endTime, TimeUnit.MILLISECONDS);
            redisTemplate.expire(contentKey,endTime, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            e.printStackTrace();
            log.info("考试安排设置失败");
        }
        log.info("考试安排设置成功");
    }

    //考试提交内容接收自动判卷队列
    @RabbitListener(queues = TASK_SUBMIT_QUEUE)
    public void taskSubmit(String content){
        ObjectMapper mapper = new ObjectMapper();
        try {
            TaskStudentSubmitVo submitVo = mapper.readValue(content, TaskStudentSubmitVo.class);
            //获取作业安排信息
            TbTaskLayout tbTaskLayout = layoutMapper.selectById(submitVo.getLayoutId());
            //获取理论作业内容
            TbTaskTheoryContent tbTaskTheoryContent = contentMapper.selectById(tbTaskLayout.getTheoryContentId());
            //获取理论作业模板
            TbTaskTheoryTemplate theoryTemplate = templateMapper.selectById(tbTaskTheoryContent.getTemplateId());
            //获取学生成绩表
            TbTaskGrade tbTaskGrade = gradeMapper.selectOne(
                    new QueryWrapper<TbTaskGrade>().lambda().eq(TbTaskGrade::getStudentId, submitVo.getStudentId())
                            .eq(TbTaskGrade::getLayoutId, submitVo.getLayoutId())
            );
            //在mongo中获取试题数据 并解析
            Query query = new Query(Criteria.where("_id").is(tbTaskTheoryContent.getTheoryMongo()));
            ContentToMongo contentToMongo = mongoTemplate.findOne(query, ContentToMongo.class);
            List<TheoryTestVo> list = mapper.readValue(contentToMongo.getContent(), new TypeReference<List<TheoryTestVo>>() {
            });

            //学生答题集合
            List<TheoryAnswerVo> answerList = submitVo.getAnswerList();
            //修改提交内容表
            String theorySubmit = mapper.writeValueAsString(answerList);
            TbTaskSubmit tbTaskSubmit = submitMapper.selectOne(
                    new QueryWrapper<TbTaskSubmit>().lambda().eq(TbTaskSubmit::getLayoutId, submitVo.getLayoutId())
                            .eq(TbTaskSubmit::getStudentId, submitVo.getStudentId())
            );
            tbTaskSubmit.setTheorySubmitContent(theorySubmit);
            tbTaskSubmit.setUpdateTime(MyTimeUtil.getNowTimeToString());
            tbTaskSubmit.setEndTime(MyTimeUtil.getNowTimeToString());
            submitMapper.updateById(tbTaskSubmit);

            //考试总分
            double sum = 0;
            if(answerList == null || answerList.size() == 0){
                //为空时0分
                for (TheoryTestVo testVo : list){
                    //理论成绩明细生成
                    TbTaskTheoryGradeDetail theoryGradeDetail = new TbTaskTheoryGradeDetail();
                    theoryGradeDetail.setGradeId(tbTaskGrade.getGradeId());
                    theoryGradeDetail.setStudentId(submitVo.getStudentId());
                    theoryGradeDetail.setTheoryId(testVo.getTheoryId());
                    theoryGradeDetail.setTheoryDetailGoal(0.0);
                    //明细信息加入数据库
                    theoryDetailMapper.insert(theoryGradeDetail);
                }
            }else {
                //生成理论明细
                for (TheoryTestVo testVo : list) {
                    //理论成绩明细生成
                    TbTaskTheoryGradeDetail theoryGradeDetail = new TbTaskTheoryGradeDetail();
                    theoryGradeDetail.setGradeId(tbTaskGrade.getGradeId());
                    theoryGradeDetail.setStudentId(submitVo.getStudentId());
                    theoryGradeDetail.setTheoryId(testVo.getTheoryId());

                    //答案比对进行判卷
                    for (TheoryAnswerVo answerVo : answerList) {
                        //根据理论ID比对
                        if(testVo.getTheoryId().equals(answerVo.getTheoryId())){
                            theoryGradeDetail.setStudentChoose(answerVo.getStudentChoose());
                            //比对答案
                            if(testVo.getAnswers().equals(answerVo.getStudentChoose())){
                                //相同则判断题型进行给分
                                switch (testVo.getTheoryType()){
                                    case "1-单选题":
                                        theoryGradeDetail.setTheoryDetailGoal(theoryTemplate.getScore1());
                                        break;
                                    case "2-多选题":
                                        theoryGradeDetail.setTheoryDetailGoal(theoryTemplate.getScore2());
                                        break;
                                    case "0-判断题":
                                        theoryGradeDetail.setTheoryDetailGoal(theoryTemplate.getScore3());
                                        break;
                                }
                                //分值累加
                                sum+=theoryGradeDetail.getTheoryDetailGoal();
                            }
                            //去除已经比对完答案的数据
                            answerList.remove(answerVo);
                            break;
                        }
                    }

                    //明细信息加入数据库
                    theoryDetailMapper.insert(theoryGradeDetail);
                }
            }

            //修改学生成绩表 考试总分
            tbTaskGrade.setTheoryGrade(sum);
            tbTaskGrade.setUpdateTime(MyTimeUtil.getNowTimeToString());
            gradeMapper.updateById(tbTaskGrade);

            log.info("考生:{}判卷成功,成绩为:{}",submitVo.getStudentId(),sum);
        } catch (Exception e) {
            e.printStackTrace();
            log.info("考生判卷失败");
        }
    }

    //考试提交内容接收自动判卷队列
    @RabbitListener(queues = TASK_START_TEST_QUEUE)
    public void taskStartTest1(String content){

        try {
            ObjectMapper mapper = new ObjectMapper();
            TaskStudentVo taskStudentVo = mapper.readValue(content, TaskStudentVo.class);

            //查询是否已经生成作业成绩表和作业提交内容表
            TbTaskGrade tbTaskGrade = gradeMapper.selectOne(
                    new QueryWrapper<TbTaskGrade>().lambda().eq(TbTaskGrade::getLayoutId, taskStudentVo.getLayoutId())
                            .eq(TbTaskGrade::getStudentId, taskStudentVo.getStudentId())
            );
            if(tbTaskGrade != null){
                log.info("学生:{} 作业成绩表已存在!",taskStudentVo.getStudentId());
            }else {
                //生成作业成绩表
                TbTaskGrade grade = new TbTaskGrade();
                grade.setStudentId(taskStudentVo.getStudentId());
                grade.setLayoutId(taskStudentVo.getLayoutId());
                grade.setClazzId(taskStudentVo.getClazzId());
                grade.setSchoolId(taskStudentVo.getSchoolId());
                gradeMapper.insert(grade);
                log.info("学生:{} 作业成绩表生成成功!",taskStudentVo.getStudentId());
            }

            TbTaskSubmit tbTaskSubmit = submitMapper.selectOne(
                    new QueryWrapper<TbTaskSubmit>().lambda().eq(TbTaskSubmit::getLayoutId, taskStudentVo.getLayoutId())
                            .eq(TbTaskSubmit::getStudentId, taskStudentVo.getStudentId())
            );
            if(tbTaskSubmit != null){
                log.info("学生:{} 作业提交内容表已存在!",taskStudentVo.getStudentId());
            }else {
                //生成作业提交内容表
                TbTaskSubmit submit = new TbTaskSubmit();
                submit.setStudentId(taskStudentVo.getStudentId());
                submit.setLayoutId(taskStudentVo.getLayoutId());
                submit.setStartTime(MyTimeUtil.getNowTimeToString());
                submit.setSchoolId(taskStudentVo.getSchoolId());
                submitMapper.insert(submit);
                log.info("学生:{} 作业提交内容表生成成功!",taskStudentVo.getStudentId());
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.info("{} 学生作业成绩表、提交表生成失败!",content);
        }
    }

    @RabbitListener(queues = TASK_START_TEST_QUEUE)
    public void taskStartTest2(String content){

        try {
            ObjectMapper mapper = new ObjectMapper();
            TaskStudentVo taskStudentVo = mapper.readValue(content, TaskStudentVo.class);

            //查询是否已经生成作业成绩表和作业提交内容表
            TbTaskGrade tbTaskGrade = gradeMapper.selectOne(
                    new QueryWrapper<TbTaskGrade>().lambda().eq(TbTaskGrade::getLayoutId, taskStudentVo.getLayoutId())
                            .eq(TbTaskGrade::getStudentId, taskStudentVo.getStudentId())
            );
            if(tbTaskGrade != null){
                log.info("学生:{} 作业成绩表已存在!",taskStudentVo.getStudentId());
            }else {
                //生成作业成绩表
                TbTaskGrade grade = new TbTaskGrade();
                grade.setStudentId(taskStudentVo.getStudentId());
                grade.setLayoutId(taskStudentVo.getLayoutId());
                grade.setClazzId(taskStudentVo.getClazzId());
                grade.setSchoolId(taskStudentVo.getSchoolId());
                gradeMapper.insert(grade);
                log.info("学生:{} 作业成绩表生成成功!",taskStudentVo.getStudentId());
            }

            TbTaskSubmit tbTaskSubmit = submitMapper.selectOne(
                    new QueryWrapper<TbTaskSubmit>().lambda().eq(TbTaskSubmit::getLayoutId, taskStudentVo.getLayoutId())
                            .eq(TbTaskSubmit::getStudentId, taskStudentVo.getStudentId())
            );
            if(tbTaskSubmit != null){
                log.info("学生:{} 作业提交内容表已存在!",taskStudentVo.getStudentId());
            }else {
                //生成作业提交内容表
                TbTaskSubmit submit = new TbTaskSubmit();
                submit.setStudentId(taskStudentVo.getStudentId());
                submit.setLayoutId(taskStudentVo.getLayoutId());
                submit.setStartTime(MyTimeUtil.getNowTimeToString());
                submit.setSchoolId(taskStudentVo.getSchoolId());
                submitMapper.insert(submit);
                log.info("学生:{} 作业提交内容表生成成功!",taskStudentVo.getStudentId());
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.info("{} 学生作业成绩表、提交表生成失败!",content);
        }
    }

    //设置作业的开始状态
    @RabbitListener(queues = SET_TASK_TEST_START_QUEUE)
    public void setTaskTestStart(String content){
        long layoutId = Long.parseLong(content);
        try {
            //更改数据库状态
            TbTaskLayout taskLayout = layoutMapper.selectById(layoutId);
            taskLayout.setTaskStatus(1);
            taskLayout.setUpdateTime(MyTimeUtil.getNowTimeToString());
            int i = layoutMapper.updateById(taskLayout);
            if(i>0){
                log.info("数据库状态修改成功");
            }
            //更改Redis中状态
            ObjectMapper mapper = new ObjectMapper();
            String value = mapper.writeValueAsString(taskLayout);

            String statusKey = "task_status_"+layoutId;
            String layoutKey = "task_layout_"+layoutId;
            redisTemplate.opsForValue().set(statusKey,"1");
            redisTemplate.opsForValue().set(layoutKey,value);

            //设置Redis中key的过期时间
            //获取当前时间戳
            long nowTime = System.currentTimeMillis();
            //考试结束时间的时间戳
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date date2 = sdf.parse(taskLayout.getEndTime());
            long endTimeToLong = date2.getTime();
            redisTemplate.expire(statusKey,(endTimeToLong-nowTime)+5000,TimeUnit.MILLISECONDS);
            redisTemplate.expire(layoutKey,(endTimeToLong-nowTime)+5000,TimeUnit.MILLISECONDS);

            log.info("Redis中状态修改成功");
        } catch (Exception e) {
            e.printStackTrace();
            log.info("{} 作业开始状态修改失败",layoutId);
        }
    }

    //设置作业的结束状态
    @RabbitListener(queues = SET_TASK_TEST_END_QUEUE)
    public void setTaskTestEnd(String content){
        long layoutId = Long.parseLong(content);
        try {
            //获取数据库状态 是否已经修改
            TbTaskLayout taskLayout = layoutMapper.selectById(layoutId);
            if(taskLayout.getTaskStatus().equals(2)){
                log.info("数据库作业结束状态已经修改");
            }else {
                taskLayout.setTaskStatus(2);
                taskLayout.setShowStatus(2);
                taskLayout.setUpdateTime(MyTimeUtil.getNowTimeToString());
                int i = layoutMapper.updateById(taskLayout);
                if(i>0){
                    log.info("数据库作业结束状态修改成功");
                }
            }
            //处理Redis中的数据
            String statusKey = "task_status_"+layoutId;
            String layoutKey = "task_layout_"+layoutId;
            redisTemplate.opsForValue().set(statusKey,"2",3,TimeUnit.SECONDS);
            if(redisTemplate.hasKey(layoutKey)){
                redisTemplate.expire(layoutKey,3,TimeUnit.SECONDS);
            }
            String showTaskTime = "show_task_time_map";
            String showTask = "show_task";
            redisTemplate.opsForSet().remove(showTask,content);
            redisTemplate.opsForHash().delete(showTaskTime,content);

            //获取考试关联的班级 从Redis中去掉相关值
            List<TbTaskClazz> clazzList = taskClazzMapper.selectList(
                    new QueryWrapper<TbTaskClazz>().lambda().eq(TbTaskClazz::getTaskId, layoutId)
            );
            if(clazzList!=null && clazzList.size() != 0){
                for (TbTaskClazz taskClazz : clazzList) {
                    String key = "task_layout_clazz_"+taskClazz.getClazzId();
                    redisTemplate.opsForSet().remove(key,content);
                }
            }

            log.info("Redis中作业结束状态处理成功");

        } catch (Exception e) {
            e.printStackTrace();
            log.info("{} 作业结束状态修改失败",layoutId);
        }
    }
}
